1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.io;
18  
19  import com.google.common.base.Charsets;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.EOFException;
24  import java.io.FilterInputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.OutputStream;
28  import java.nio.channels.Channels;
29  import java.nio.channels.ReadableByteChannel;
30  import java.nio.channels.WritableByteChannel;
31  import java.util.Arrays;
32  
33  /**
34   * Unit test for {@link ByteStreams}.
35   *
36   * @author Chris Nokleberg
37   */
38  public class ByteStreamsTest extends IoTestCase {
39  
40    public void testCopyChannel() throws IOException {
41      byte[] expected = newPreFilledByteArray(100);
42      ByteArrayOutputStream out = new ByteArrayOutputStream();
43      WritableByteChannel outChannel = Channels.newChannel(out);
44  
45      ReadableByteChannel inChannel =
46          Channels.newChannel(new ByteArrayInputStream(expected));
47      ByteStreams.copy(inChannel, outChannel);
48      assertEquals(expected, out.toByteArray());
49    }
50  
51    public void testReadFully() throws IOException {
52      byte[] b = new byte[10];
53  
54      try {
55        ByteStreams.readFully(newTestStream(10), null, 0, 10);
56        fail("expected exception");
57      } catch (NullPointerException e) {
58      }
59  
60      try {
61        ByteStreams.readFully(null, b, 0, 10);
62        fail("expected exception");
63      } catch (NullPointerException e) {
64      }
65  
66      try {
67        ByteStreams.readFully(newTestStream(10), b, -1, 10);
68        fail("expected exception");
69      } catch (IndexOutOfBoundsException e) {
70      }
71  
72      try {
73        ByteStreams.readFully(newTestStream(10), b, 0, -1);
74        fail("expected exception");
75      } catch (IndexOutOfBoundsException e) {
76      }
77  
78      try {
79        ByteStreams.readFully(newTestStream(10), b, 0, -1);
80        fail("expected exception");
81      } catch (IndexOutOfBoundsException e) {
82      }
83  
84      try {
85        ByteStreams.readFully(newTestStream(10), b, 2, 10);
86        fail("expected exception");
87      } catch (IndexOutOfBoundsException e) {
88      }
89  
90      try {
91        ByteStreams.readFully(newTestStream(5), b, 0, 10);
92        fail("expected exception");
93      } catch (EOFException e) {
94      }
95  
96      Arrays.fill(b, (byte) 0);
97      ByteStreams.readFully(newTestStream(10), b, 0, 0);
98      assertEquals(new byte[10], b);
99  
100     Arrays.fill(b, (byte) 0);
101     ByteStreams.readFully(newTestStream(10), b, 0, 10);
102     assertEquals(newPreFilledByteArray(10), b);
103 
104     Arrays.fill(b, (byte) 0);
105     ByteStreams.readFully(newTestStream(10), b, 0, 5);
106     assertEquals(new byte[]{0, 1, 2, 3, 4, 0, 0, 0, 0, 0}, b);
107   }
108 
109   public void testSkipFully() throws IOException {
110     byte[] bytes = newPreFilledByteArray(100);
111     skipHelper(0, 0, new ByteArrayInputStream(bytes));
112     skipHelper(50, 50, new ByteArrayInputStream(bytes));
113     skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 1));
114     skipHelper(50, 50, new SlowSkipper(new ByteArrayInputStream(bytes), 0));
115     skipHelper(100, -1, new ByteArrayInputStream(bytes));
116     try {
117       skipHelper(101, 0, new ByteArrayInputStream(bytes));
118       fail("expected exception");
119     } catch (EOFException e) {
120     }
121   }
122 
123   private static void skipHelper(long n, int expect, InputStream in)
124       throws IOException {
125     ByteStreams.skipFully(in, n);
126     assertEquals(expect, in.read());
127     in.close();
128   }
129 
130   private static final byte[] bytes =
131       new byte[] { 0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10 };
132 
133   public void testNewDataInput_empty() {
134     byte[] b = new byte[0];
135     ByteArrayDataInput in = ByteStreams.newDataInput(b);
136     try {
137       in.readInt();
138       fail("expected exception");
139     } catch (IllegalStateException expected) {
140     }
141   }
142 
143   public void testNewDataInput_normal() {
144     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
145     assertEquals(0x12345678, in.readInt());
146     assertEquals(0x76543210, in.readInt());
147     try {
148       in.readInt();
149       fail("expected exception");
150     } catch (IllegalStateException expected) {
151     }
152   }
153 
154   public void testNewDataInput_readFully() {
155     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
156     byte[] actual = new byte[bytes.length];
157     in.readFully(actual);
158     assertEquals(bytes, actual);
159   }
160   
161   public void testNewDataInput_readFullyAndThenSome() {
162     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
163     byte[] actual = new byte[bytes.length * 2];
164     try {
165       in.readFully(actual);
166       fail("expected exception");
167     } catch (IllegalStateException ex) {
168       assertTrue(ex.getCause() instanceof EOFException);
169     }
170   }
171   
172   public void testNewDataInput_readFullyWithOffset() {
173     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
174     byte[] actual = new byte[4];
175     in.readFully(actual, 2, 2);
176     assertEquals(0, actual[0]);
177     assertEquals(0, actual[1]);
178     assertEquals(bytes[0], actual[2]);
179     assertEquals(bytes[1], actual[3]);
180   }
181   
182   public void testNewDataInput_readLine() {
183     ByteArrayDataInput in = ByteStreams.newDataInput(
184         "This is a line\r\nThis too\rand this\nand also this".getBytes(Charsets.UTF_8));
185     assertEquals("This is a line", in.readLine());
186     assertEquals("This too", in.readLine());
187     assertEquals("and this", in.readLine());
188     assertEquals("and also this", in.readLine());
189   }
190 
191   public void testNewDataInput_readFloat() {
192     byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
193     ByteArrayDataInput in = ByteStreams.newDataInput(data);
194     assertEquals(Float.intBitsToFloat(0x12345678), in.readFloat(), 0.0);
195     assertEquals(Float.intBitsToFloat(0x76543210), in.readFloat(), 0.0);
196   }
197   
198   public void testNewDataInput_readDouble() {
199     byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
200     ByteArrayDataInput in = ByteStreams.newDataInput(data);
201     assertEquals(Double.longBitsToDouble(0x1234567876543210L), in.readDouble(), 0.0);
202   }
203 
204   public void testNewDataInput_readUTF() {
205     byte[] data = new byte[17];
206     data[1] = 15;
207     System.arraycopy("Kilroy was here".getBytes(Charsets.UTF_8), 0, data, 2, 15);
208     ByteArrayDataInput in = ByteStreams.newDataInput(data);
209     assertEquals("Kilroy was here", in.readUTF());
210   }
211 
212   public void testNewDataInput_readChar() {
213     byte[] data = "qed".getBytes(Charsets.UTF_16BE);
214     ByteArrayDataInput in = ByteStreams.newDataInput(data);
215     assertEquals('q', in.readChar());
216     assertEquals('e', in.readChar());
217     assertEquals('d', in.readChar());
218   }
219   
220   public void testNewDataInput_readUnsignedShort() {
221     byte[] data = {0, 0, 0, 1, (byte) 0xFF, (byte) 0xFF, 0x12, 0x34};
222     ByteArrayDataInput in = ByteStreams.newDataInput(data);
223     assertEquals(0, in.readUnsignedShort());
224     assertEquals(1, in.readUnsignedShort());
225     assertEquals(65535, in.readUnsignedShort());
226     assertEquals(0x1234, in.readUnsignedShort());
227   }
228   
229   public void testNewDataInput_readLong() {
230     byte[] data = {0x12, 0x34, 0x56, 0x78, 0x76, 0x54, 0x32, 0x10};
231     ByteArrayDataInput in = ByteStreams.newDataInput(data);
232     assertEquals(0x1234567876543210L, in.readLong());
233   }
234 
235   public void testNewDataInput_readBoolean() {
236     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
237     assertTrue(in.readBoolean());
238   }
239   
240   public void testNewDataInput_readByte() {
241     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
242     for (int i = 0; i < bytes.length; i++) {
243       assertEquals(bytes[i], in.readByte());
244     }
245     try {
246       in.readByte();
247       fail("expected exception");
248     } catch (IllegalStateException ex) {
249       assertTrue(ex.getCause() instanceof EOFException);
250     }
251   }
252 
253   public void testNewDataInput_readUnsignedByte() {
254     ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
255     for (int i = 0; i < bytes.length; i++) {
256       assertEquals(bytes[i], in.readUnsignedByte());
257     }
258     try {
259       in.readUnsignedByte();
260       fail("expected exception");
261     } catch (IllegalStateException ex) {
262       assertTrue(ex.getCause() instanceof EOFException);
263     }
264   }
265 
266   public void testNewDataInput_offset() {
267     ByteArrayDataInput in = ByteStreams.newDataInput(bytes, 2);
268     assertEquals(0x56787654, in.readInt());
269     try {
270       in.readInt();
271       fail("expected exception");
272     } catch (IllegalStateException expected) {
273     }
274   }
275 
276   public void testNewDataInput_skip() {
277     ByteArrayDataInput in = ByteStreams.newDataInput(new byte[2]);
278     assertEquals(2, in.skipBytes(2));
279     assertEquals(0, in.skipBytes(1));
280   }
281 
282   public void testNewDataInput_BAIS() {
283     ByteArrayInputStream bais = new ByteArrayInputStream(new byte[] {0x12, 0x34, 0x56, 0x78});
284     ByteArrayDataInput in = ByteStreams.newDataInput(bais);
285     assertEquals(0x12345678, in.readInt());
286   }
287 
288   public void testNewDataOutput_empty() {
289     ByteArrayDataOutput out = ByteStreams.newDataOutput();
290     assertEquals(0, out.toByteArray().length);
291   }
292 
293   public void testNewDataOutput_writeInt() {
294     ByteArrayDataOutput out = ByteStreams.newDataOutput();
295     out.writeInt(0x12345678);
296     out.writeInt(0x76543210);
297     assertEquals(bytes, out.toByteArray());
298   }
299 
300   public void testNewDataOutput_sized() {
301     ByteArrayDataOutput out = ByteStreams.newDataOutput(4);
302     out.writeInt(0x12345678);
303     out.writeInt(0x76543210);
304     assertEquals(bytes, out.toByteArray());
305   }
306 
307   public void testNewDataOutput_writeLong() {
308     ByteArrayDataOutput out = ByteStreams.newDataOutput();
309     out.writeLong(0x1234567876543210L);
310     assertEquals(bytes, out.toByteArray());
311   }
312 
313   public void testNewDataOutput_writeByteArray() {
314     ByteArrayDataOutput out = ByteStreams.newDataOutput();
315     out.write(bytes);
316     assertEquals(bytes, out.toByteArray());
317   }
318 
319   public void testNewDataOutput_writeByte() {
320     ByteArrayDataOutput out = ByteStreams.newDataOutput();
321     out.write(0x12);
322     out.writeByte(0x34);
323     assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
324   }
325 
326   public void testNewDataOutput_writeByteOffset() {
327     ByteArrayDataOutput out = ByteStreams.newDataOutput();
328     out.write(bytes, 4, 2);
329     byte[] expected = {bytes[4], bytes[5]};
330     assertEquals(expected, out.toByteArray());
331   }
332 
333   public void testNewDataOutput_writeBoolean() {
334     ByteArrayDataOutput out = ByteStreams.newDataOutput();
335     out.writeBoolean(true);
336     out.writeBoolean(false);
337     byte[] expected = {(byte) 1, (byte) 0};
338     assertEquals(expected, out.toByteArray());
339   }
340 
341   public void testNewDataOutput_writeChar() {
342     ByteArrayDataOutput out = ByteStreams.newDataOutput();
343     out.writeChar('a');
344     assertEquals(new byte[] {0, 97}, out.toByteArray());
345   }
346 
347   public void testNewDataOutput_writeChars() {
348     ByteArrayDataOutput out = ByteStreams.newDataOutput();
349     out.writeChars("r\u00C9sum\u00C9");
350     // need to remove byte order mark before comparing
351     byte[] expected = Arrays.copyOfRange("r\u00C9sum\u00C9".getBytes(Charsets.UTF_16), 2, 14);
352     assertEquals(expected, out.toByteArray());
353   }
354 
355   public void testNewDataOutput_writeUTF() {
356     ByteArrayDataOutput out = ByteStreams.newDataOutput();
357     out.writeUTF("r\u00C9sum\u00C9");
358     byte[] expected ="r\u00C9sum\u00C9".getBytes(Charsets.UTF_8);
359     byte[] actual = out.toByteArray();
360     // writeUTF writes the length of the string in 2 bytes
361     assertEquals(0, actual[0]);
362     assertEquals(expected.length, actual[1]);
363     assertEquals(expected, Arrays.copyOfRange(actual, 2, actual.length));
364   }
365 
366   public void testNewDataOutput_writeShort() {
367     ByteArrayDataOutput out = ByteStreams.newDataOutput();
368     out.writeShort(0x1234);
369     assertEquals(new byte[] {0x12, 0x34}, out.toByteArray());
370   }
371 
372   public void testNewDataOutput_writeDouble() {
373     ByteArrayDataOutput out = ByteStreams.newDataOutput();
374     out.writeDouble(Double.longBitsToDouble(0x1234567876543210L));
375     assertEquals(bytes, out.toByteArray());
376   }
377 
378   public void testNewDataOutput_writeFloat() {
379     ByteArrayDataOutput out = ByteStreams.newDataOutput();
380     out.writeFloat(Float.intBitsToFloat(0x12345678));
381     out.writeFloat(Float.intBitsToFloat(0x76543210));
382     assertEquals(bytes, out.toByteArray());
383   }
384 
385   public void testNewDataOutput_BAOS() {
386     ByteArrayOutputStream baos = new ByteArrayOutputStream();
387     ByteArrayDataOutput out = ByteStreams.newDataOutput(baos);
388     out.writeInt(0x12345678);
389     assertEquals(4, baos.size());
390     assertEquals(new byte[] {0x12, 0x34, 0x56, 0x78}, baos.toByteArray());
391   }
392 
393   public void testToByteArray_withSize_givenCorrectSize() throws IOException {
394     InputStream in = newTestStream(100);
395     byte[] b = ByteStreams.toByteArray(in, 100);
396     assertEquals(100, b.length);
397   }
398 
399   public void testToByteArray_withSize_givenSmallerSize() throws IOException {
400     InputStream in = newTestStream(100);
401     byte[] b = ByteStreams.toByteArray(in, 80);
402     assertEquals(100, b.length);
403   }
404 
405   public void testToByteArray_withSize_givenLargerSize() throws IOException {
406     InputStream in = newTestStream(100);
407     byte[] b = ByteStreams.toByteArray(in, 120);
408     assertEquals(100, b.length);
409   }
410 
411   public void testToByteArray_withSize_givenSizeZero() throws IOException {
412     InputStream in = newTestStream(100);
413     byte[] b = ByteStreams.toByteArray(in, 0);
414     assertEquals(100, b.length);
415   }
416 
417   private static InputStream newTestStream(int n) {
418     return new ByteArrayInputStream(newPreFilledByteArray(n));
419   }
420 
421   /** Stream that will skip a maximum number of bytes at a time. */
422   private static class SlowSkipper extends FilterInputStream {
423     private final long max;
424 
425     public SlowSkipper(InputStream in, long max) {
426       super(in);
427       this.max = max;
428     }
429 
430     @Override public long skip(long n) throws IOException {
431       return super.skip(Math.min(max, n));
432     }
433   }
434 
435   public void testReadBytes() throws IOException {
436     final byte[] array = newPreFilledByteArray(1000);
437     assertEquals(array, ByteStreams.readBytes(
438       new ByteArrayInputStream(array), new TestByteProcessor()));
439   }
440 
441   private class TestByteProcessor implements ByteProcessor<byte[]> {
442     private final ByteArrayOutputStream out = new ByteArrayOutputStream();
443 
444     @Override
445     public boolean processBytes(byte[] buf, int off, int len)
446         throws IOException {
447       out.write(buf, off, len);
448       return true;
449     }
450 
451     @Override
452     public byte[] getResult() {
453       return out.toByteArray();
454     }
455   }
456 
457   public void testByteProcessorStopEarly() throws IOException {
458     byte[] array = newPreFilledByteArray(6000);
459     assertEquals((Integer) 42,
460         ByteStreams.readBytes(new ByteArrayInputStream(array),
461             new ByteProcessor<Integer>() {
462               @Override
463               public boolean processBytes(byte[] buf, int off, int len) {
464                 assertEquals(
465                     copyOfRange(buf, off, off + len),
466                     newPreFilledByteArray(4096));
467                 return false;
468               }
469 
470               @Override
471               public Integer getResult() {
472                 return 42;
473               }
474             }));
475   }
476 
477   public void testNullOutputStream() throws Exception {
478     // create a null output stream
479     OutputStream nos = ByteStreams.nullOutputStream();
480     // write to the output stream
481     nos.write('n');
482     String test = "Test string for NullOutputStream";
483     nos.write(test.getBytes());
484     nos.write(test.getBytes(), 2, 10);
485     // nothing really to assert?
486     assertSame(ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream());
487   }
488 
489   public void testLimit() throws Exception {
490     byte[] big = newPreFilledByteArray(5);
491     InputStream bin = new ByteArrayInputStream(big);
492     InputStream lin = ByteStreams.limit(bin, 2);
493 
494     // also test available
495     lin.mark(2);
496     assertEquals(2, lin.available());
497     int read = lin.read();
498     assertEquals(big[0], read);
499     assertEquals(1, lin.available());
500     read = lin.read();
501     assertEquals(big[1], read);
502     assertEquals(0, lin.available());
503     read = lin.read();
504     assertEquals(-1, read);
505 
506     lin.reset();
507     byte[] small = new byte[5];
508     read = lin.read(small);
509     assertEquals(2, read);
510     assertEquals(big[0], small[0]);
511     assertEquals(big[1], small[1]);
512 
513     lin.reset();
514     read = lin.read(small, 2, 3);
515     assertEquals(2, read);
516     assertEquals(big[0], small[2]);
517     assertEquals(big[1], small[3]);
518   }
519 
520   public void testLimit_mark() throws Exception {
521     byte[] big = newPreFilledByteArray(5);
522     InputStream bin = new ByteArrayInputStream(big);
523     InputStream lin = ByteStreams.limit(bin, 2);
524 
525     int read = lin.read();
526     assertEquals(big[0], read);
527     lin.mark(2);
528 
529     read = lin.read();
530     assertEquals(big[1], read);
531     read = lin.read();
532     assertEquals(-1, read);
533 
534     lin.reset();
535     read = lin.read();
536     assertEquals(big[1], read);
537     read = lin.read();
538     assertEquals(-1, read);
539   }
540 
541   public void testLimit_skip() throws Exception {
542     byte[] big = newPreFilledByteArray(5);
543     InputStream bin = new ByteArrayInputStream(big);
544     InputStream lin = ByteStreams.limit(bin, 2);
545 
546     // also test available
547     lin.mark(2);
548     assertEquals(2, lin.available());
549     lin.skip(1);
550     assertEquals(1, lin.available());
551 
552     lin.reset();
553     assertEquals(2, lin.available());
554     lin.skip(3);
555     assertEquals(0, lin.available());
556   }
557   
558   public void testLimit_markNotSet() {
559     byte[] big = newPreFilledByteArray(5);
560     InputStream bin = new ByteArrayInputStream(big);
561     InputStream lin = ByteStreams.limit(bin, 2);
562 
563     try {
564       lin.reset();
565       fail();
566     } catch (IOException expected) {
567       assertEquals("Mark not set", expected.getMessage());
568     }
569   }
570   
571   public void testLimit_markNotSupported() {
572     InputStream lin = ByteStreams.limit(new UnmarkableInputStream(), 2);
573 
574     try {
575       lin.reset();
576       fail();
577     } catch (IOException expected) {
578       assertEquals("Mark not supported", expected.getMessage());
579     }
580   }
581 
582   private static class UnmarkableInputStream extends InputStream {
583     @Override
584     public int read() throws IOException {
585       return 0;
586     }
587 
588     @Override
589     public boolean markSupported() {
590       return false;
591     }
592   }
593 
594   private static byte[] copyOfRange(byte[] in, int from, int to) {
595     byte[] out = new byte[to - from];
596     for (int i = 0; i < to - from; i++) {
597       out[i] = in[from + i];
598     }
599     return out;
600   }
601 
602   private static void assertEquals(byte[] expected, byte[] actual) {
603     assertTrue(Arrays.equals(expected, actual));
604   }
605 }